package com.dirox.confluence.labelmanagement.action;

import com.atlassian.confluence.event.Evented;
import com.atlassian.confluence.event.events.label.LabelListViewEvent;
import com.atlassian.confluence.labels.Label;
import com.atlassian.confluence.labels.actions.AbstractLabelDisplayingAction;
import com.atlassian.confluence.spaces.SpaceManager;
import com.atlassian.confluence.user.SessionKeys;
import com.atlassian.confluence.util.actions.AlphabeticalLabelGroupingSupport;

import com.dirox.confluence.labelmanagement.service.LabelManagementService;
import com.dirox.confluence.labelmanagement.utils.SpaceNotFoundException;
import static com.google.common.base.Preconditions.checkNotNull;

import com.opensymphony.xwork.ActionContext;

import org.apache.commons.lang.StringUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;


/**
 * This class displays labels management
 */
public class ViewLabelManagement extends AbstractLabelDisplayingAction
    implements Evented<LabelListViewEvent> {
    private static final long serialVersionUID = 1L;
    private AlphabeticalLabelGroupingSupport alphabeticalLabelGroupingSupport = null;
    private LabelManagementService labelManagementService;
    private SpaceManager spaceManager;

    /**
     * Get map with key is labelName and value is the number of contents
     * associated to label
     *
     * @param spaceKey
     * @return Map<String, Integer>
     * @throws SpaceNotFoundException
     */
    public Map<Label, Integer> countNumberOfAffectedContent(String spaceKey)
        throws SpaceNotFoundException {
        Map<Label, Integer> results = new HashMap<Label, Integer>();

        if (StringUtils.isNotBlank(spaceKey)) {
            if (spaceManager.getSpace(spaceKey) == null) {
                throw new SpaceNotFoundException(getText(
                        "label-management.error.not.exist.space"));
            }

            results = labelManagementService.getLabelsCountInSpace(spaceKey);
        } else {
            results = labelManagementService.getLabelsCountInAllSpaces();
        }

        return sortByComparator(results);
    }

    @Override
    public LabelListViewEvent getEventToPublish(String result) {
        return new LabelListViewEvent(this, getSpace(), "LabelManagement");
    }

    public AlphabeticalLabelGroupingSupport getAlphabeticalLabelGroupingSupport() {
        if (alphabeticalLabelGroupingSupport == null) {
            Set<Label> labels = null;

            if (getSpace() != null) {
                labels = labelManagementService.getLabelsCountInSpace(getSpace()
                                                                          .getKey())
                                               .keySet();
            } else {
                labels = labelManagementService.getLabelsCountInAllSpaces()
                                               .keySet();
            }

            alphabeticalLabelGroupingSupport = new AlphabeticalLabelGroupingSupport(labels);
        }

        return alphabeticalLabelGroupingSupport;
    }

    public boolean isGroupRanks() {
        Boolean groupRanks = (Boolean) ActionContext.getContext().getSession()
                                                    .get(SessionKeys.HEATMAP_GROUP_RANKS);

        return (groupRanks != null) && groupRanks.booleanValue();
    }

    @SuppressWarnings("unchecked")
    public void setGroupRanks(boolean b) {
        ActionContext.getContext().getSession()
                     .put(SessionKeys.HEATMAP_GROUP_RANKS, Boolean.valueOf(b));
    }

    public void setLabelManagementService(
        LabelManagementService labelManagementService) {
        this.labelManagementService = checkNotNull(labelManagementService);
    }

    public void setSpaceManager(SpaceManager spaceManager) {
        this.spaceManager = checkNotNull(spaceManager);
    }

    /**
     * Sort a Map by Values
     *
     * @param unsortMap
     * @return sortedMap
     */
    private Map<Label, Integer> sortByComparator(Map<Label, Integer> unsortMap) {
        List<Entry<Label, Integer>> list = new LinkedList<Entry<Label, Integer>>(unsortMap.entrySet());

        // sort list based on comparator
        Collections.sort(list,
            new Comparator<Entry<Label, Integer>>() {
                @Override
                public int compare(Entry<Label, Integer> o1,
                    Entry<Label, Integer> o2) {
                    return (o1.getValue() > o2.getValue()) ? (-1) : 1;
                }
            });

        // put sorted list into map again
        // LinkedHashMap make sure order in which keys were inserted
        Map<Label, Integer> sortedMap = new LinkedHashMap<Label, Integer>();

        for (Iterator<Entry<Label, Integer>> it = list.iterator();
                it.hasNext();) {
            Map.Entry<Label, Integer> entry = it.next();
            sortedMap.put(entry.getKey(), entry.getValue());
        }

        return sortedMap;
    }
}
